#include "Python.h"
#include <sys/mman.h> 
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <arpa/inet.h>



#define MAX_FILE_NAME_LEN 256
#define MAX_DICT_NO 32

static struct dict_info{
    int id;
    FILE *idx_fd;
    char *idx_data;
    FILE *dict_fd;
    long file_size;
    long wc;
    char idx_file_name[MAX_FILE_NAME_LEN];
    char dict_file_name[MAX_FILE_NAME_LEN];
    char gz_file_name[MAX_FILE_NAME_LEN];
}dict_list[MAX_DICT_NO];

static int cur_dict_no = 0;
static int initialized = 0;

static void init_dicts();
static void init_dict();
static void close_dict(struct dict_info* dict);
static struct dict_info* get_first_available_dict();
static int load_idx();

static PyObject * load(PyObject *self, PyObject *args)
{
}

static PyObject * create_dict(PyObject *self, PyObject *args)
{
    char *file_prefix;
    int ret;

    if (!initialized){
        init_dicts();
        initialized = 1;
    }
    if (cur_dict_no>=MAX_DICT_NO)
        return Py_BuildValue("i",-1);

    struct dict_info *d = get_first_available_dict();
    //must be non-null
    fprintf(stderr,"get dict id:%d\n",d->id);


    if (!PyArg_ParseTuple(args, "sll:create_dict",
                &file_prefix, &(d->file_size), &(d->wc))) {
        fprintf(stderr,"parse args failed\n");
        return NULL;
    }

    strcpy(d->idx_file_name, file_prefix);
    strcat(d->idx_file_name, ".idx");

    strcpy(d->dict_file_name, file_prefix);
    strcat(d->dict_file_name, ".dict");

    strcpy(d->gz_file_name, file_prefix);
    strcat(d->gz_file_name, ".dict.dz");

    if ( (ret = load_idx(d)) < 0 ) {
        close_dict(d);
        return Py_BuildValue("i",ret);
    }
    cur_dict_no++;
    return Py_BuildValue("i",d->id);

}

static PyObject * remove_dict(PyObject *self, PyObject *args)
{
    int dict_id;
    if (!PyArg_ParseTuple(args, "i:remove_dict", &dict_id)) {
        fprintf(stderr,"parse args failed\n");
        return Py_BuildValue("i",-1);
    }
    if (dict_id>=MAX_DICT_NO){
        fprintf(stderr,"Illegal dict id:%d\n",dict_id);
        return Py_BuildValue("i",-1);
    }

    struct dict_info *d = &(dict_list[dict_id]);
    close_dict(d);
    cur_dict_no--;
    return Py_BuildValue("i",0);
}

static void init_dicts()
{
    int i = 0;
    for (; i<MAX_DICT_NO; i++){
        init_dict( &(dict_list[i]) );
        dict_list[i].id = i;
    }
    cur_dict_no = 0;
}

static void init_dict(struct dict_info* dict)
{
    dict->idx_fd = NULL;
    dict->idx_data = NULL;
    dict->dict_fd = NULL;
    dict->idx_file_name[0] = '\0';
    dict->dict_file_name[0] = '\0';
    dict->gz_file_name[0] = '\0';
}

static void close_dict(struct dict_info* dict)
{
    if ( dict->idx_fd != NULL )
        fclose(dict->idx_fd);
    if ( dict->idx_data != NULL )
        free(dict->idx_data);
    if ( dict->dict_fd != NULL )
        fclose(dict->dict_fd);
    init_dict(dict);
}

static struct dict_info* get_first_available_dict()
{
    int i = 0;
    for (; i<MAX_DICT_NO; i++){
        if ( dict_list[i].idx_fd==NULL )
            return &(dict_list[i]);
    }
    return NULL;
}

static int load_idx(struct dict_info *d)
{
    int n_read;
    if ((d->idx_fd = fopen(d->idx_file_name, "r")) == NULL) {
        fprintf(stderr,"open failed\n");
        return -1;
    }
    d->idx_data = malloc(d->file_size);
    if ( (n_read = fread( d->idx_data, 1, d->file_size, d->idx_fd )) != d->file_size ){
        fprintf(stderr,"read idx file error, no of bytes read:%d size:%ld\n", n_read, d->file_size);
        return -1;
    }

    if ((d->dict_fd = fopen(d->dict_file_name, "r")) ==NULL) {
        fprintf(stderr,"open dict failed\n");
        fprintf(stderr,"%s\n",d->dict_file_name);
        return -1;
    }

    return 0;
}

static struct dict_info* get_dict_by_id(int id)
{
    if ( id>=MAX_FILE_NAME_LEN){
        return NULL;
    }
    return &(dict_list[id]);
}

static int is_dict_valid(struct dict_info *d)
{
    return ( (d->idx_fd!=NULL) && (d->idx_data!=NULL) &&
            (d->dict_fd!=NULL) );
}

static PyObject * lookup(PyObject *self, PyObject *args)
{
    int dict_id;
    char *word;
    struct dict_info *d = NULL;

    const char *p;
    long index_size;
    int offset;
    int size;
    int found = 0;
    unsigned char *buf;
    PyObject* ret = NULL;

    if (!PyArg_ParseTuple(args, "is:lookup",
                &dict_id, &word)) {
        fprintf(stderr,"parse args failed\n");
        return NULL;
    }

    d = get_dict_by_id(dict_id);
    if ( (d==NULL) || (!is_dict_valid(d)) )
        return NULL;

    p = d->idx_data;
    int i;
    for (i=0;i<d->wc;i++) {
        index_size = strlen(p) + 1 + 2*sizeof(int);
        if (strcmp(word, p) == 0) {
            found = 1;
        }
        if (found == 1) {
            offset = ntohl(*(int *)(p + strlen(p) + 1));
            size   = ntohl(*(int *)(p + strlen(p) + 1 + sizeof(int)));

            fseek(d->dict_fd, offset, SEEK_SET);
            buf = (unsigned char *)malloc(size+1);
            memset(buf, '\0', size+1);
            fread(buf, 1, size, d->dict_fd);
            /*fprintf(stderr,"%sn",buf);*/
            ret = Py_BuildValue("s", buf);
            free(buf);
            return ret;
        }
        p += index_size;
    }

    return Py_BuildValue("s","");

}



static struct PyMethodDef lookup_methods[] = {
    {"lookup", lookup, METH_VARARGS, "lookup(dict_id, word)"},
    {"create_dict", create_dict, METH_VARARGS, "create_dict(file_prefix, file_size, wc)"},
    {"remove_dict", remove_dict, METH_VARARGS, "remove_dict(dict_id)"},
    {NULL, NULL, 0, NULL}
};

    PyMODINIT_FUNC
initlookup(void)
{
    (void) Py_InitModule("lookup", lookup_methods);
}


